package com.tedu.aop.demo;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 定义一个切面，用于某方法之前或之后或两者同时 动态加入一些逻辑
 *
 * 定义一个切面需要做两件事
 * 1.添加@Component注解，该类必须受Spring容器管理
 * 2.添加@Aspect注解，声明这是一个切面类
 * */
@Component
@Aspect
public class LogAspect {
    /**
     * @Before注解用于标志当前方法的切入点，是在指定切入点(某方法)之前执行
     * 该注解需要传入一个参数，用于指定切入点信息(即目标方法)，要描述方法的签名
     *
     * 切入点描述的格式:
     * execution(方法签名)
     *
     * 方法签名的格式:
     * public int com.tedu.aop.demo.CalculatorImpl.add(..)
     * 修饰符 返回值 类的完全限定名.方法名(参数列表)
     *
     * 其中参数列表可以使用".."，表示任意参数
     *
     * 其他部分也可以使用通配符"*"
     * 例如，如果希望在CalculatorImpl任意方法前进行操作可以将方法名用“*”代替
     * execution(public int com.tedu.aop.demo.CalculatorImpl.*(..))
     *
     */
//    @Before("execution(public int com.tedu.aop.demo.CalculatorImpl.add(int,int))")
//    @Before("execution(public double com.tedu.aop.demo.CalculatorImpl.add(double,double))")

    //如果方法签名定义与实际方法不一致，则不发生任何作用
//    @Before("execution(public double com.tedu.aop.demo.CalculatorImpl.add(int,double))")//方法签名不一致
//    @Before("execution(public int com.tedu.aop.demo.CalculatorImpl.add(double,double))")//返回值类型不一致

    //返回值类型使用*时，可以不明确返回值类型
//    @Before("execution(public * com.tedu.aop.demo.CalculatorImpl.add(double,double))")
//    @Before("execution(public * com.tedu.aop.demo.CalculatorImpl.add(..))")

    //如果方法的参数是引用类型时，要写清楚类的完全限定名(包名.类名)
//    @Before("execution(public * com.tedu.aop.demo.CalculatorImpl.add(java.util.List))")
    //由于String是在java.lang包中，该包默认导入，因此不用写包名
//    @Before("execution(public * com.tedu.aop.demo.CalculatorImpl.add(String))")

//    @Before("execution(public int com.tedu.aop.demo.CalculatorImpl.add(..))")
//    @Before("execution(public int com.tedu.aop.demo.CalculatorImpl.*(..))")

    //无论返回值类型是什么，方法名叫什么，参数列表是什么，只要是CalculatorImpl中的方法，前面都要切入
//    @Before("execution(public * com.tedu.aop.demo.CalculatorImpl.*(..))")
//    public void before(JoinPoint joinPoint) {
//        /*
//            在当前方法上可以声明一个参数:JoinPoint，该对象记录了"切入点"的信息
//            这里切入点就是我们当前逻辑动态添加到哪个方法上，那个方法就是切入点
//         */
//        //getTarget()获取当前切入的方法所属的对象，这里就是:CalculatorImpl的实例
////        Object obj = joinPoint.getTarget();
////        System.out.println("target:"+obj);
//        //通过该对象获取其对应类的类对象
////        Class cls = obj.getClass();
//        //通过类对象获取该类的完全限定名(包名.类名)
////        System.out.println(cls.getName());
//        //仅获取类名
////        System.out.println(cls.getSimpleName());
//
//        //通过切入点对象获取方法签名(获取到切入对应方法的信息)
////        Signature signature = joinPoint.getSignature();
//        //通过方法签名对象获取该方法的名字
////        String methodName = signature.getName();
////        System.out.println("方法:"+methodName);
////        System.out.println("方法信息:"+signature);
//
//        //获取参数
////        Object[] args = joinPoint.getArgs();
////        System.out.println("参数:"+ Arrays.toString(args));
////
////        System.out.println("开始调用add方法");
//
//        Object obj = joinPoint.getTarget();
//        Class cls = obj.getClass();
//        String className = cls.getSimpleName();
//        Signature signature = joinPoint.getSignature();
//        String methodName = signature.getName();
//        System.out.println("开始调用"+className+"."+methodName+"()方法");
//    }
//
//    /**
//     * @AfterReturning注解
//     * 该切入时机是在实际方法调用完毕并且返回值后进行
//     */
//    @AfterReturning(
//        value = "execution(public * com.tedu.aop.demo.CalculatorImpl.*(..))",
//        returning = "returnValue" //用于声明实际方法返回值传递给当前切面方法的哪个参数
//    )
//    public void afterReturning(JoinPoint joinPoint,Object returnValue) {
//        //获取类名
//        String className = joinPoint.getTarget().getClass().getSimpleName();
//        //获取方法名
//        String methodName = joinPoint.getSignature().getName();
//
//        System.out.println("调用"+className+"."+methodName+"()方法完毕，返回值为:"+returnValue);
//    }
//
//    @AfterThrowing(value = "execution(public * com.tedu.aop.demo.CalculatorImpl.*(..))",
//            throwing = "e"//用于声明实际方法抛出的异常传递给当前切面方法的哪个参数上
//    )
//    public void afterThrowing(JoinPoint joinPoint, Exception e) {
//        //获取类名
//        String className = joinPoint.getTarget().getClass().getSimpleName();
//        //获取方法名
//        String methodName = joinPoint.getSignature().getName();
//
//        System.out.println("调用"+className+"."+methodName+"()方法发生异常:"+e.getMessage());
//    }
//
//    @After("execution(public * com.tedu.aop.demo.CalculatorImpl.*(..))")
//    /**
//     * @After注解
//     * 切入点是在方法执行完毕后执行
//     * 无论方法是正常执行完毕还是抛出异常，该方法都会执行
//     * */
//    public void after(JoinPoint joinPoint) {
//        //获取类名
//        String className = joinPoint.getTarget().getClass().getSimpleName();
//        //获取方法名
//        String methodName = joinPoint.getSignature().getName();
//
//        System.out.println("调用"+className+"."+methodName+"()方法最终执行完毕!!！!！！!！！！！!！！！！！!！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！");
//    }
    @Around(value = "execution(public * com.tedu.aop.demo.CalculatorImpl.*(..))")
    public Object around(ProceedingJoinPoint joinPoint){
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();

        try {
            //等同于@Before
            System.out.println("调用"+className+"."+methodName+"()方法执行开始!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
            //使用切入点对象调用实际方法
            Object returnValue = joinPoint.proceed();
            //等同于@AfterReturning
            System.out.println("调用"+className+"."+methodName+"()方法执行完毕,返回值为"+returnValue);
            return returnValue;
        }catch (Throwable e) {
            //等同于@AfterThrowing
            System.out.println("调用"+className+"."+methodName+"()方法发生异常,异常信息为"+e.getMessage());
            throw new RuntimeException(e);
        }finally {
            //等同于@After
            System.out.println("调用"+className+"."+methodName+"()方法最终执行完毕!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
        }
    }
}
